package com.example.service;

import com.example.data.crawler.WebCrawler;
import com.example.data.entity.CleanedData;
import com.example.data.entity.RawData;
import com.example.data.entity.kg.KgEntity;

import com.example.data.entity.PreprocessedData;
import com.example.data.mapper.*;
import com.example.util.LlmClientUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import com.google.gson.Gson;

import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 秦朝数据服务层：协调爬取、清洗和知识图谱构建
 */
@Service
public class DataService {
    private static final Logger log = LoggerFactory.getLogger(DataService.class);

    // 文本清洗正则：去除多余空格、换行和特殊符号
    private static final Pattern SPECIAL_CHAR_PATTERN = Pattern.compile("[\\t\\r\\n\\s]{2,}");
    private static final Pattern NOISE_PATTERN = Pattern.compile("[^a-zA-Z0-9\\u4e00-\\u9fa5，。,.:;？?！!()（）]");

    // LLM提示词模板（用于实体和关系提取）
    private static final String ENTITY_EXTRACT_PROMPT = """
            请从以下秦朝史料文本中提取实体，并按类型分类（人物/事件/地点/政权）。
            输出格式：实体名称,实体类型,实体描述（从文本中提取的相关描述）
            文本：%s
            """;

    private static final String RELATION_EXTRACT_PROMPT = """
            请从以下秦朝史料文本中提取实体间的关系，已知实体列表：%s
            输出格式：主体实体名称,关系类型（如“建立”“统治”“发生于”）,客体实体名称,关系描述
            文本：%s
            """;

    private final Gson gson = new Gson();

    @Resource
    private WebCrawler crawler;

    @Resource
    private RawDataMapper rawDataMapper;

    @Resource
    private CleanedDataMapper cleanedDataMapper;

    @Resource
    private KgEntityMapper entityMapper;

    @Resource
    private PreprocessedDataMapper preprocessedDataMapper;

    @Resource
    private KgRelationMapper relationMapper;

    @Resource
    private LlmClientUtil llmClientUtil;

    /**
     * 爬取并保存秦朝史料
     */
    @Transactional(rollbackFor = Exception.class)
    public int crawlAndSaveQinData() throws Exception {
        log.info("开始爬取秦朝史料：{}", WebCrawler.QIN_ROOT_URL);
        List<RawData> rawDataList = crawler.crawlAllQinChapters();
        if (rawDataList.isEmpty()) {
            log.warn("未爬取到任何数据");
            return 0;
        }
        log.info("爬取到 {} 个章节，开始代码层去重", rawDataList.size());

        // 2. 代码层去重：查询数据库中已存在的URL，过滤掉重复数据
        List<String> existUrls = rawDataMapper.selectExistUrls(); // 新增Mapper方法：查询所有已存在的source_url
        List<RawData> newDataList = rawDataList.stream()
                .filter(data -> !existUrls.contains(data.getSourceUrl()))
                .collect(Collectors.toList());

        if (newDataList.isEmpty()) {
            log.info("所有爬取数据已存在于数据库，无需插入");
            return 0;
        }
        log.info("去重后需插入 {} 个新章节", newDataList.size());

        // 3. 批量插入新数据
        rawDataMapper.batchInsert(newDataList);
        log.info("爬取完成，共插入 {} 个章节", newDataList.size());
        return newDataList.size();
    }

    /**
     * 清洗原始数据：去除噪声、标准化格式，为知识抽取做准备
     */
    @Transactional(rollbackFor = Exception.class)
    public int cleanData() {
        log.info("开始清洗秦朝史料原始数据");
        // 1. 查询未清洗的原始数据（已爬取且有效）
        List<RawData> rawDataList = rawDataMapper.selectUncleanedData();
        if (rawDataList.isEmpty()) {
            log.warn("无未清洗数据");
            return 0;
        }

        int successCount = 0;
        for (RawData raw : rawDataList) {
            try {
                CleanedData cleaned = new CleanedData();
                cleaned.setRawId(raw.getId());

                // 2. 清洗标题（去除特殊符号、多余空格）
                cleaned.setCleanedTitle(cleanText(raw.getChapterTitle()));

                // 3. 清洗原文（核心：保留历史文本完整性，仅去除噪声）
                cleaned.setCleanedOriginal(cleanHistoricalText(raw.getOriginalText()));

                // 4. 清洗译文（若存在）
                if (raw.getTranslation() != null) {
                    cleaned.setCleanedTranslation(cleanHistoricalText(raw.getTranslation()));
                }

                // 5. 记录清洗时间
                cleaned.setProcessTime(LocalDateTime.now());

                // 6. 保存到cleaned_data表
                cleanedDataMapper.insert(cleaned);
                successCount++;
            } catch (Exception e) {
                log.error("清洗数据失败，raw_id={}", raw.getId(), e);
            }
        }

        log.info("数据清洗完成，共处理{}条，成功{}条", rawDataList.size(), successCount);
        return successCount;
    }

    /**
     * 通用文本清洗：去除特殊符号、标准化空格
     */
    private String cleanText(String text) {
        if (text == null || text.trim().isEmpty()) {
            return "";
        }
        String cleaned = text.trim()
                // 去除HTML标签残留（如&lt;、&gt;等）
                .replaceAll("&[a-zA-Z]+;", " ")
                // 去除多余空格、换行、制表符
                .replaceAll("\\s+", " ")
                // 去除无意义特殊符号（保留中文、英文、数字及基本标点）
                .replaceAll("[^\\u4e00-\\u9fa5a-zA-Z0-9，。,.:;？?！!()（）]", " ");
        return cleaned.trim();
    }

    /**
     * 历史文本专用清洗：额外处理历史文献特有格式（如纪年、人名后缀）
     */
    private String cleanHistoricalText(String text) {
        String cleaned = cleanText(text);
        // 处理历史文本特有噪声（示例：去除章节标记如"【卷一】"、"[注]"等）
        cleaned = cleaned.replaceAll("【.*?】", " ")  // 去除【】及内部内容
                .replaceAll("\\[注.*?\\]", " ")  // 去除[注...]
                .replaceAll("\\s+", " ");  // 再次合并空格
        return cleaned;
    }

    /**
     * 对清洗后的数据进行预处理（分句、实体预标注等）
     */
    @Transactional(rollbackFor = Exception.class)
    public int preprocessCleanedData() {
        log.info("开始预处理清洗后的数据");

        // 1. 查询未预处理的清洗数据（每次处理100条，避免内存溢出）
        List<CleanedData> cleanedList = preprocessedDataMapper.selectUnprocessedCleanedData();
        if (cleanedList.isEmpty()) {
            log.warn("无未预处理的清洗数据");
            return 0;
        }

        // 2. 加载实体词典（从kg_entity表或配置文件）
        Map<String, String> entityDict = loadEntityDictionary();

        int successCount = 0;
        for (CleanedData cleaned : cleanedList) {
            try {
                PreprocessedData preData = new PreprocessedData();
                preData.setCleanedId(cleaned.getId());

                // 3. 选择处理文本（优先用译文，无则用原文）
                String text = cleaned.getCleanedTranslation() != null ?
                        cleaned.getCleanedTranslation() : cleaned.getCleanedOriginal();

                // 4. 核心预处理步骤
                // 4.1 分句（按中文标点拆分）
                List<String> sentences = splitIntoSentences(text);
                // 4.2 过滤无效句子（如长度过短的）
                List<String> validSentences = filterInvalidSentences(sentences);
                // 4.3 实体预标注（用词典标记已知实体）
                String annotatedText = annotateEntities(String.join("。", validSentences), entityDict);

                // 5. 存储结果（句子列表转JSON）
                preData.setSentences(new Gson().toJson(validSentences)); // 用Gson序列化列表
                preData.setAnnotatedText(annotatedText);
                preData.setProcessTime(LocalDateTime.now());

                // 6. 入库
                preprocessedDataMapper.insert(preData);
                // 7. 标记清洗数据为“已预处理”
                preprocessedDataMapper.markAsPreprocessed(cleaned.getId());

                successCount++;
            } catch (Exception e) {
                log.error("预处理失败，cleaned_id={}", cleaned.getId(), e);
            }
        }

        log.info("预处理完成，成功处理{}条数据", successCount);
        return successCount;
    }

    /**
     * 步骤1：分句（按中文标点拆分）
     */
    private List<String> splitIntoSentences(String text) {
        if (text == null || text.trim().isEmpty()) {
            return new ArrayList<>();
        }
        // 按中文句末标点拆分（。？！；）
        String[] parts = text.split("[。？！;；]");
        List<String> sentences = new ArrayList<>();
        for (String part : parts) {
            String sentence = part.trim();
            if (!sentence.isEmpty()) {
                sentences.add(sentence);
            }
        }
        return sentences;
    }

    /**
     * 步骤2：过滤无效句子（如长度过短、无意义内容）
     */
    private List<String> filterInvalidSentences(List<String> sentences) {
        List<String> valid = new ArrayList<>();
        for (String sen : sentences) {
            // 过滤长度<5的句子（避免无意义片段）
            if (sen.length() >= 5) {
                valid.add(sen);
            }
        }
        return valid;
    }

    /**
     * 步骤3：实体预标注（用【实体<类型>】格式标记）
     */
    private String annotateEntities(String text, Map<String, String> entityDict) {
        String annotated = text;
        // 按实体长度倒序替换（避免短实体被长实体包含时漏标）
        List<Map.Entry<String, String>> sortedEntities = new ArrayList<>(entityDict.entrySet());
        sortedEntities.sort((e1, e2) -> Integer.compare(e2.getKey().length(), e1.getKey().length()));

        for (Map.Entry<String, String> entry : sortedEntities) {
            String entity = entry.getKey();
            String type = entry.getValue();
            // 用正则替换（避免部分匹配，如“秦”不应匹配“秦朝”）
            annotated = annotated.replaceAll(
                    "(?<!\\w)" + Pattern.quote(entity) + "(?!\\w)",
                    "【" + entity + "<" + type + ">】"
            );
        }
        return annotated;
    }

    /**
     * 加载实体词典（从kg_entity表查询已提取的实体）
     */
    private Map<String, String> loadEntityDictionary() {
        List<KgEntity> entities = entityMapper.selectAll();
        Map<String, String> dict = new HashMap<>();
        for (KgEntity entity : entities) {
            // 实体名称→实体类型（如“嬴政”→“人物”）
            dict.put(entity.getEntityName(), entity.getEntityType());
        }
        return dict;
    }

    @Transactional(rollbackFor = Exception.class)
    public int extractEntities(PreprocessedData preprocessedData) {
        // 1. 准备输入文本：优先用“实体预标注文本”（降低LLM抽取难度）
        String inputText = preprocessedData.getAnnotatedText();
        if (inputText == null || inputText.trim().isEmpty()) {
            inputText = gson.fromJson(preprocessedData.getSentences(), List.class).toString();
        }

        // 2. 构造实体抽取Prompt（引导LLM输出CSV格式，含置信度）
        String prompt = String.format("""
                任务：从以下秦朝史料文本中提取实体，仅支持4种类型：人物、事件、地点、政权。
                输出格式：必须为CSV，字段顺序：实体名称,实体类型,实体描述,置信度（0-1）
                要求：
                1. 实体描述需从文本中提取具体信息（如“嬴政：秦朝开国皇帝，公元前221年统一中国”）；
                2. 置信度：基于文本相关性打分（如明确提及则0.9+，间接推断则0.6-0.8）；
                3. 排除重复实体（同一名称+类型视为同一实体）；
                4. 若无实体，输出“无”。
                文本：%s
                """, inputText);

        // 3. 调用LLM获取结果
        String llmResponse = llmClientUtil.callLlm(prompt);
        if (llmResponse == null || llmResponse.trim().isEmpty() || llmResponse.contains("无")) {
            log.info("该文本无实体可抽取，preprocessed_id={}", preprocessedData.getId());
            return 0;
        }

        // 4. 解析LLM结果为KgEntity列表
        List<KgEntity> entityList = parseEntityResponse(llmResponse, preprocessedData);
        if (entityList.isEmpty()) {
            log.warn("实体解析失败，preprocessed_id={}", preprocessedData.getId());
            return 0;
        }

        // 5. 批量插入实体（依赖KgEntityMapper的batchInsert，已有）
        entityMapper.batchInsert(entityList);
        log.info("实体抽取完成，preprocessed_id={}，抽取数量={}", preprocessedData.getId(), entityList.size());
        return entityList.size();
    }

    /**
     * 解析LLM输出的CSV格式实体结果
     * @param llmResponse LLM返回的CSV文本
     * @param preprocessedData 来源预处理数据
     * @return 结构化的KgEntity列表
     */
    private List<KgEntity> parseEntityResponse(String llmResponse, PreprocessedData preprocessedData) {
        List<KgEntity> entityList = new ArrayList<>();
        String[] lines = llmResponse.split("\\n");  // 按行分割CSV

        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("实体名称")) {  // 跳过表头或空行
                continue;
            }

            // 按逗号分割字段（处理描述中含逗号的情况：用最后一个逗号分割置信度）
            int lastCommaIndex = line.lastIndexOf(",");
            if (lastCommaIndex == -1 || lastCommaIndex == line.length() - 1) {
                log.warn("无效实体行：{}", line);
                continue;
            }

            // 提取字段
            String basePart = line.substring(0, lastCommaIndex).trim();
            String confidenceStr = line.substring(lastCommaIndex + 1).trim();
            String[] baseFields = basePart.split(",", 3);  // 最多分割3段（处理描述含逗号）

            if (baseFields.length != 3) {
                log.warn("实体字段数量错误：{}", line);
                continue;
            }

            String entityName = baseFields[0].trim();
            String entityType = baseFields[1].trim();
            String entityDesc = baseFields[2].trim();
            Double confidence;

            // 校验字段有效性
            try {
                confidence = Double.parseDouble(confidenceStr);
                if (confidence < 0 || confidence > 1) {
                    throw new IllegalArgumentException("置信度超出范围");
                }
                // 仅保留支持的实体类型
                if (!List.of("人物", "事件", "地点", "政权").contains(entityType)) {
                    log.warn("不支持的实体类型：{}，行：{}", entityType, line);
                    continue;
                }
            } catch (Exception e) {
                log.warn("实体字段校验失败：{}，错误：{}", line, e.getMessage());
                continue;
            }

            // 构造KgEntity对象（关联来源数据）
            KgEntity entity = new KgEntity();
            entity.setEntityName(entityName);
            entity.setEntityType(entityType);
            entity.setEntityDesc(entityDesc);
            entity.setConfidence(confidence);
            // 关联来源：预处理数据→清洗数据→原始数据
            entity.setSourceCleanedId(preprocessedData.getCleanedId());
            entity.setSourceRawId(cleanedDataMapper.selectById(preprocessedData.getCleanedId()).getRawId());
            entity.setCreateTime(LocalDateTime.now());
            entity.setIsValid(1);

            entityList.add(entity);
        }

        return entityList;
    }


}
